package com.jml.jdk8.stream;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;

/**
 * @author:jml
 * @date: 2020/2/10 0010 19:30
 * @description：
 */
public class 短路和并发流分组分区 {


    /**
     * 串行流-stream
     */
    @Test
    public void 串行流() {
        ArrayList<String> list = new ArrayList<>(5000000);
        for (int i = 0; i < 5000000; i++) {
            list.add(UUID.randomUUID().toString());
        }
        System.out.println("开始排序");
        //表示纳秒高于精度毫秒
        long startTime = System.nanoTime();
        //count是终止操作，在这里无意义，本质是统计个数
        long count = list.stream().sorted().count();
        long endTime = System.nanoTime();
        //把纳秒转化为毫秒
        long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
        System.out.println("排序耗时：" + millis);
    }

    /**
     * 并行流-parallelStream 底层是多线程，使用forkJoin，大任务拆分为多个小任务
     */
    @Test
    public void 并行流() {
        ArrayList<String> list = new ArrayList<>(5000000);
        for (int i = 0; i < 5000000; i++) {
            list.add(UUID.randomUUID().toString());
        }
        System.out.println("开始排序");
        //表示纳秒高于精度毫秒
        long startTime = System.nanoTime();
        //count是终止操作，在这里无意义，本质是统计个数
        long count = list.parallelStream().sorted().count();
        long endTime = System.nanoTime();
        //把纳秒转化为毫秒
        long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
        System.out.println("排序耗时：" + millis);
    }

    /**
     * 短路 findFirst找出第一个元素 找出列表长度为5的第一个单词,打印他的长度
     */
    @Test
    public void 短路() {
        List<String> list = Arrays.asList("hello", "world", "hello world");
        //打印第一个单词
        list.stream().filter(item -> item.length() == 5).limit(1).forEach(System.out::println);
        System.out.println("1---------------------------------------------------------------");

        //打印第一个单词的长度
        list.stream().mapToInt(item -> item.length()).filter(length -> length == 5).findFirst()
                .ifPresent(System.out::println);
        System.out.println("2---------------------------------------------------------------");

        //在mapToInt中的输出只会输出到第二个单词，后面的单词是不会输出，也就是不会遍历到后面的单词
        //mapToInt里面的遍历并不会将全部的元素遍历，因为后面接了一个filter条件和findFirst的条件只找第一个元素
        //当把这些操作全部放入容器后mapToInt中遍历只有找到第一个满足条件的元素就不会再进行遍历了。
        list.stream().mapToInt(item -> {
            int length = item.length();
            System.out.println(item);
            return length;
        }).filter(length -> length == 5).findFirst()
                .ifPresent(System.out::println);
    }


    /**
     * 找出这个集合中去重后的所有的单词 flatMap操作元素为数组
     */
    @Test
    public void flatMap操作元素为数组() {
        List<String> list = Arrays
                .asList("hello world", "hello welcome", "world hello", "hello world hello",
                        "hello world");
        //找出这个集合中去重后的所有的单词
        list.stream().map(item -> item.split(" "))
                .flatMap(array -> Stream.of(array)).distinct().forEach(System.out::println);
    }

    /**
     * 两个集合，得到结果是交叉的
     */
    @Test
    public void flatMap两个集合操作() {
        List<String> list1 = Arrays.asList("hello", "hi", "你好");
        List<String> list2 = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
        //不生成新的集合
        list1.forEach(item -> list2.forEach(it -> System.out.println(it + " " + item)));
        //生成新的集合
        list1.stream().flatMap(item -> list2.stream().map(item2 -> item + item2))
                .collect(Collectors.toList())
                .forEach(System.out::println);
    }

    /**
     * groupingBy分组转化为map 参数是Function接口 Collectors.counting()得到个数
     * 1.将list转化为map,key为name，value是一个集合,集合中的对象是student
     * 2.将list转化为map,key为name，value是集合中元素的个数
     * 3.将list转化为map,根据name进行分组,value是list中score的平均值
     */
    @Test
    public void groupingBy分组转化为map() {
        Student student1 = new Student("zhangsan", 100, 20);
        Student student2 = new Student("lisi", 90, 20);
        Student student3 = new Student("wangwu", 90, 30);
        Student student4 = new Student("zhaoliu", 80, 40);
        List<Student> students = Arrays.asList(student1, student2, student3, student4);
        //Function<Student, String> getName = Student::getName;
        //Function<Student, String> getName1 = (Student s) -> s.getName();
        //根据name进行分组
        Map<String, List<Student>> nameMap = students.stream()
                .collect(Collectors.groupingBy(Student::getName));
        System.out.println(nameMap);
        //根据分数进行分组
        Map<Integer, List<Student>> scoreMap = students.stream()
                .collect(Collectors.groupingBy(s -> s.getScore()));
        //----------------------------------------------------------------------------------------------
        //根据name进行分组,value是list的个数
        Map<String, Long> countMap = students.stream()
                .collect(Collectors.groupingBy(s -> s.getName(), Collectors.counting()));
        //根据name进行分组,value是list中score的平均值
        Map<String, Double> collect = students.stream()
                .collect(Collectors.groupingBy(
                        s -> s.getName(), Collectors.averagingDouble(s -> s.getScore())
                ));
    }

    /**
     * 分区：partition By
     * partitioningBy中的参数是predicate
     * 分组有多个情况，分区结果只有两个分区
     * 90以下一个区，90以上一个区，key只有true和false所以只有两个区
     */
    public void partitioningBy进行分区(){
        Student student1 = new Student("zhangsan", 100, 20);
        Student student2 = new Student("lisi", 90, 20);
        Student student3 = new Student("wangwu", 90, 30);
        Student student4 = new Student("zhaoliu", 80, 40);
        List<Student> students = Arrays.asList(student1, student2, student3, student4);
        //90以下一个区，90以上一个区
        Map<Boolean, List<Student>> collect = students.stream()
                .collect(Collectors.partitioningBy(s -> s.getScore() >= 90));
    }

}
